package com.navinfo.opentsp.qingqi.trackoptimize.service;


import com.mongodb.*;
import com.navinfo.opentsp.qingqi.trackoptimize.commands.QueryTripDetailCommand;
import com.navinfo.opentsp.qingqi.trackoptimize.commands.QueryTripPbInfoCommand;
import com.navinfo.opentsp.qingqi.trackoptimize.commands.QueryTripPbPageInfoCommand;
import com.navinfo.opentsp.qingqi.trackoptimize.util.DateUtil;
import com.navinfo.opentsp.qingqi.trackoptimize.util.MongoUtil;
import com.navinfo.opentsp.qingqi.trackoptimize.util.StringUtil;
import com.navinfo.opentsp.qingqi.trackoptimize.entity.MongoTripDataEntity;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 行程查询服务
 * create by wangshuai 2017-03-17
 */
@Service
public class TripQueryService {

    protected static final Logger logger = LoggerFactory.getLogger(TripQueryService.class);

    @Autowired
    private TrackQueryService trackQueryService;

    @Autowired
    private MongoTemplate mongoTemplate;

    String collectionName = "car_trip_info";

    Map<String, String> suggestMap = new HashMap<String, String>() {
        {
            put("1", "速度偏高");
            put("2", "速度略高");
            put("3", "速度稳定,路况通畅");
            put("4", "速度略低,部分路段不通畅");
            put("5", "速度偏低,部分路段不通畅");
            put("6", "建议平稳驾驶");
            put("7", "驾驶习惯急待改进");
            put("8", "注意不良驾驶习惯");
            put("9", "驾驶习惯良好");
            put("10", "能耗较高,请注意养成良好驾驶习惯");
            put("11", "能耗偏高,请注意养成良好驾驶习惯");
            put("12", "能耗略高,请注意养成良好驾驶习惯");
            put("13", "能耗正常,请保持良好驾驶习惯");
            put("14", "能耗低,请保持良好驾驶习惯");
        }
    };


    /**
     * 根据时间段查各天行程数量
     *
     * @param command
     * @return
     */
    public List<Map> queryTrip(QueryTripPbInfoCommand command) {
        Map<String, String> tableMap = new HashMap<>();
        Long beginTime = DateUtil.parseDate(command.getBeginDate(), DateUtil.time_pattern).getTime();
        Long endTime = DateUtil.parseDate(command.getEndDate(), DateUtil.time_pattern).getTime();
        Long beginZeroTime = DateUtil.getZeroTime(beginTime);
        Long endZeroTime = DateUtil.getZeroTime(endTime);
        Long tempTime = beginZeroTime;
        Calendar cal = Calendar.getInstance();
        cal.setTime(new Date(beginTime));
        int week = cal.get(Calendar.DAY_OF_WEEK) - 1;
        if (week == 1) {
            //如果开始时间是周一，添加开始时间的上周表，避免跨周行程遗漏
            tableMap.put(collectionName + MongoUtil.getTripTableName(new Date(beginTime - 604800000)), "");
        }
        //确定要查询的mongo表
        while (tempTime < endZeroTime) {
            tableMap.put(collectionName + MongoUtil.getTripTableName(new Date(tempTime)), "");
            tempTime += 86400000;
        }
        long ks = System.currentTimeMillis();
        //组装查询条件
        BasicDBObject[] array = {
                //最终组装
                new BasicDBObject("_auto_terminal", command.getTerminalId()),
                new BasicDBObject("_trip_time", new BasicDBObject("$gte", 300000L)),
                new BasicDBObject("_trip_len", new BasicDBObject("$gte", 1d)),
                new BasicDBObject("_total_useoil", new BasicDBObject("$gt", 0d)),
                new BasicDBObject("_avghundoil", new BasicDBObject("$gt", 0d)),
                new BasicDBObject("_avghundoil", new BasicDBObject("$lt", 80d)),
                new BasicDBObject("_start_time", new BasicDBObject("$lt", endTime)),
                new BasicDBObject("_end_time", new BasicDBObject("$gte", beginTime))

        };

        BasicDBObject cond = new BasicDBObject();
        cond.put("$and", array);

        List<Map> returnList = new ArrayList<>();
        Map<String, Integer> countMap = new HashMap<>();
        for (Map.Entry<String, String> entry : tableMap.entrySet()) {
            DBObject fields = new BasicDBObject(); //设置需要获取哪些域
            fields.put("_id", 0);
            fields.put("_start_time", 1);
            fields.put("_end_time", 1);
            DBCursor dbCursor = mongoTemplate.getCollection(entry.getKey()).find(cond, fields);
            while (dbCursor.hasNext()) {
                DBObject object = dbCursor.next();
                long start_long = (Long) object.get("_start_time");
                long end_long = (Long) object.get("_end_time");
                String start = "";
                //进行循环防止跨两天以上行程数计算有误
                while (end_long > start_long) {
                    start = DateUtil.format(DateUtil.date_s_pattern, new Date(start_long));
                    Integer count = countMap.get(start);
                    if (count == null) {
                        countMap.put(start, 1);
                    } else {
                        countMap.put(start, ++count);
                    }
                    //向前加1天
                    start_long += 86400000;
                }
                String end = DateUtil.format(DateUtil.date_s_pattern, new Date(end_long));
                if (!start.equals(end)) {
                    Integer count2 = countMap.get(end);
                    if (count2 == null) {
                        countMap.put(end, 1);
                    } else {
                        countMap.put(end, ++count2);
                    }
                }
            }
        }
        for (Map.Entry<String, Integer> entry : countMap.entrySet()) {
            Map map = new HashMap<>();
            map.put("tripDate", entry.getKey());
            map.put("tripCount", entry.getValue());
            returnList.add(map);
        }

        logger.info("=========query day`s group trip count：" + (System.currentTimeMillis() - ks) + " ms=========");
        //进行排序
        Collections.sort(returnList, new Comparator() {
            public int compare(Object a, Object b) {
                long oneStartTime = DateUtil.parseDate(String.valueOf(((Map) a).get("tripDate")), DateUtil.date_s_pattern).getTime();
                long twoStartTime = DateUtil.parseDate(String.valueOf(((Map) b).get("tripDate")), DateUtil.date_s_pattern).getTime();
                return (int) (oneStartTime - twoStartTime);
            }
        });
        return returnList;
    }

    /**
     * 根据时间段查询行程列表（分页）
     *
     * @param command
     * @return
     */
    public Map queryPageTrip(QueryTripPbPageInfoCommand command) {
        long ks = System.currentTimeMillis();
        Long beginTime = DateUtil.parseDate(command.getBeginDate(), DateUtil.time_pattern).getTime();
        Long endTime = DateUtil.parseDate(command.getEndDate(), DateUtil.time_pattern).getTime();
        if (beginTime > endTime) {
            return new HashMap();
        }
        Query count_query = new Query(Criteria.where("_auto_terminal").is(command.getTerminalId())
                .and("_trip_time").gte(300000L)
                .and("_trip_len").gte(1d)
                .and("_total_useoil").gt(0d)
                .and("_avghundoil").gt(0d).lt(80d)
                .and("_start_time").lt(endTime)
                .and("_end_time").gte(beginTime)
        );
        count_query.fields().include("_id");
        List<String> tableList = MongoUtil.getTripTableList(collectionName, command.getBeginDate(), command.getEndDate());
        //默认多查一个上周表，避免跨多天遗漏
        tableList.add(collectionName + MongoUtil.getTripTableName(new Date(beginTime - 604800000)));

        long total = queryMongoCount(count_query, tableList);//记录总数
        long zs = System.currentTimeMillis();
        logger.info("=========query trip page count：" + (zs - ks) + " ms=========");
        int page_number = Integer.parseInt(command.getPage_number());//查询条件：页码
        int page_size = Integer.parseInt(command.getPage_size());//查询条件：每页记录数
        Query data_query = new Query(Criteria.where("_auto_terminal").is(command.getTerminalId())
                .and("_trip_time").gte(300000L)
                .and("_trip_len").gte(1d)
                .and("_total_useoil").gt(0d)
                .and("_avghundoil").gt(0d).lt(80d)
                .and("_start_time").lt(endTime)
                .and("_end_time").gte(beginTime)
        );
        data_query.fields().exclude("_data");
        data_query.skip((page_number - 1) * page_size);
        data_query.limit(page_size);
        data_query.with(new Sort(Sort.Direction.DESC, "_start_time"));
        Map returnMap = new HashMap();
        returnMap.put("total", total);
        List<MongoTripDataEntity> tripList = new ArrayList<>();
        if (total > 0) {
            tripList = trackQueryService.queryMongo(data_query, tableList);
        }
        returnMap.put("data", tripList);
        logger.info("=========query trip page data：" + (System.currentTimeMillis() - zs) + " ms=========");
        return returnMap;

    }

    /**
     * 根据kid查询行程详情
     *
     * @param command
     * @return
     */
    public MongoTripDataEntity queryTripInfo(QueryTripDetailCommand command) {
        Query query = new Query(Criteria.where("_kid").is(command.getTripId()));
        query.fields().exclude("_data");
        List<MongoTripDataEntity> tripList = null;
        //如果行程时间有值
        if (StringUtil.isNotEmpty(command.getTripDate())) {
            String collectionSuffix = MongoUtil.getTripTableName(DateUtil.parseDate(command.getTripDate(), DateUtil.date_s_pattern));
            //查询mongo
            tripList = mongoTemplate.find(query, MongoTripDataEntity.class, collectionName + collectionSuffix);
            if (tripList == null || tripList.size() == 0) {
                long beginTime = DateUtil.parseDate(command.getTripDate(), DateUtil.date_s_pattern).getTime();
                //查询mongo
                tripList = mongoTemplate.find(query, MongoTripDataEntity.class, collectionName + MongoUtil.getTripTableName(new Date(beginTime - 604800000)));
            }
        } else {
            //如果行程时间为空，查询半年数据，兼容原接口
            Calendar calendar = Calendar.getInstance();
            calendar.setTime(new Date());
            calendar.add(Calendar.WEEK_OF_YEAR, -24);
            List<String> tableList = MongoUtil.getTripTableList(collectionName, DateUtil.formatTime(calendar.getTime()), DateUtil.formatTime(new Date()));
            tripList = trackQueryService.queryMongo(query, tableList);
        }
        if (tripList != null && tripList.size() > 0) {
            MongoTripDataEntity tripDataPbEntity = tripList.get(0);
            if (containChinese(tripDataPbEntity.get_drive_suggest()) == 0 && containChinese(tripDataPbEntity.get_oil_suggest()) == 0) {
                //填充建议语言
                String[] driverSug = tripDataPbEntity.get_drive_suggest().split(",");
                String[] oilSug = tripDataPbEntity.get_oil_suggest().split(",");
                String driverSug_str = "";
                String oilSug_str = "";
                for (String dSug : driverSug) {
                    driverSug_str += suggestMap.get(dSug) + ",";
                }
                for (String oSug : oilSug) {
                    oilSug_str += suggestMap.get(oSug) + ",";
                }
                tripDataPbEntity.set_drive_suggest(StringUtil.removeLastStr(driverSug_str, ","));
                tripDataPbEntity.set_oil_suggest(StringUtil.removeLastStr(oilSug_str, ","));
            }
            return tripDataPbEntity;
        } else {
            return null;
        }
    }

    /**
     * 多线程查询各周表记录条数汇总后返回
     *
     * @param query
     * @param list
     * @return
     */
    public Long queryMongoCount(final Query query, final List<String> list) {
        long count = 0l;
        if (list == null || list.size() == 0) {
            return count;
        }
        List<Callable<Long>> callables = new ArrayList<>();
        //根据需要查询表个数创建callable
        for (int i = 0; i < list.size(); i++) {
            final int finalI = i;
            Callable<Long> callable;
            callable = new Callable<Long>() {
                public Long call() throws Exception {
                    return getResult(query, list.get(finalI));
                }
            };
            callables.add(callable);
        }

        ExecutorService executor = Executors.newFixedThreadPool(list.size());

        try {
            //执行mongo查询
            List<Future<Long>> futures = executor.invokeAll(callables);
            //拼装为一个list
            for (Future<Long> future : futures) {
                count += future.get();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            executor.shutdown();
        }
        return count;
    }

    private Long getResult(Query query, String tn) {
        return mongoTemplate.count(query, tn);//记录总数
    }

    /**
     * 计算字符串中包含几个中文
     *
     * @param arg
     * @return
     */
    public int containChinese(String arg) {
        int count = 0;
        if (arg != null) {
            String regEx = "[\\u4e00-\\u9fa5]";
            Pattern p = Pattern.compile(regEx);
            Matcher m = p.matcher(arg);
            while (m.find()) {
                for (int i = 0; i <= m.groupCount(); i++) {
                    count = count + 1;
                }
            }
        }
        return count;
    }

}